package com.coderside.fragment.thread.ABCD;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @author: yanxuxin
 * @date: 2010-2-18
 */
public class MultiThreadWriter {

	public static String[] FILE_NAMES = { "A", "B", "C", "D" };

	/** 文件路径 */
	public static String FILE_PATH = "D:" + File.separator;

	private final List<MockOutput> outList = new ArrayList<MockOutput>(
			FILE_NAMES.length);

	/** 平衡优先级的比较器 */
	private final Comparator<MockOutput> comparator = new PriorityComparator();

	/** 线程运行开关 */
	private volatile boolean working = true;

	public static void main(String[] args) {
		long a = System.currentTimeMillis();
		final MultiThreadWriter writer = new MultiThreadWriter();
		final CountDownLatch start = new CountDownLatch(1); // 发令信号
		final CountDownLatch end = new CountDownLatch(16 * 100); // 终止信号

		for (int i = 0; i < FILE_NAMES.length; i++) {
			writer.new WriteWorker(writer, i + 1, start, end).start(); // 开启4个线程，每个线程分别只写入单一值
		}

		start.countDown(); // 熟悉的开闸放狗

		try {
			end.await(); // 等待其他子线程工作
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		writer.cleanAndDisplay(); // 计数为0时,打印最后一次调整了存储顺序的StringBuilder的值
		long b = System.currentTimeMillis();
		System.out.println(b - a);
	}

	public MultiThreadWriter() {
		try {
			init();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public boolean isWorking() {
		return working;
	}

	/**
	 * 供子线程调用并写入线程绑定的数值给文件和StringBuilder
	 * 
	 * @param num
	 * @return
	 */
	public boolean writeToOutput(int num) {
		synchronized (outList) {
			for (int i = 0; i < FILE_NAMES.length; i++) {
				MockOutput output = outList.get(i);

				int lastest = output.getTheLatestNum();
				if ((lastest % FILE_NAMES.length) + 1 == num) { // 取此output最后一次写入的值,判断是否满足再次写入
					output.append(num);
					output.setTheLatestNum(num);

					adjustPriority(); // 调整outList中各MockOutput实例的顺序

					return true;
				}
			}
		}
		return false;
	}

	public void cleanAndDisplay() {
		working = false;
		synchronized (outList) {
			for (int i = 0; i < FILE_NAMES.length; i++) {
				MockOutput out = outList.get(i);
				out.close();

				System.out.println(out.toString());
			}
		}
	}

	/**
	 * 初始化每个MockOutput实例的最后一次写入值,并把其加入到outList中
	 * 
	 * @throws IOException
	 */
	private void init() throws IOException {
		for (int i = 0; i < FILE_NAMES.length; i++) {
			MockOutput output = new MockOutput(64, FILE_NAMES[i], FILE_PATH);
			output.setTheLatestNum(i);
			outList.add(output);
		}
	}

	/**
	 * 使用Comparator的自身实现对outList内的对象进行排序,其作用在于平衡写入优先级.
	 * 例如:线程Thread-2取得了outList对象的锁执行写入,其绑定的值为3,而outList中
	 * 有两个MockOutput实例的最后一次写入都为2,均满足再次写入的条件.但是其中一个的长度
	 * 远远大于另一个,并且其在outList内的位置又在另一个之前,则其将先于后者获得再次写 入得机会,使得两者的机会不均衡.
	 * 此方法的作用就在于把写入机会少的MockOutput调整到最前面从而提高其被写入的机会.
	 */
	private void adjustPriority() {
		Collections.sort(outList, comparator);
	}

	private class WriteWorker extends Thread {

		/** 多个线程共享同一个MultiThreadWriter实例 */
		private MultiThreadWriter writer;

		/** 线程绑定的写入值,每个线程只反复写入一个固定值 */
		private final int num;

		private final CountDownLatch start;
		private final CountDownLatch end;

		public WriteWorker(MultiThreadWriter writer, int num,
				CountDownLatch start, CountDownLatch end) {
			this.writer = writer;
			this.num = num;
			this.start = start;
			this.end = end;
		}

		public void run() {
			try {
				start.await();// 等待主线程一声令下,所有的子线程均唤醒开始工作
				doWork();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		/**
		 * 子线程写入固定值,写入成功则递减计数器
		 * 
		 * @throws InterruptedException
		 */
		private void doWork() throws InterruptedException {
			while (writer.isWorking()) {
				boolean isWrited = writer.writeToOutput(num);
				if (isWrited) {
					end.countDown();
					Thread.sleep(1); // 调整线程竞争,使得每个线程获取outList锁的几率均衡
				}
			}
		}
	}

	private class MockOutput {

		/** 用于终端显示的记录 */
		private final StringBuilder stringBuilder;

		/** 需要写入的文本输出 */
		private final PrintWriter printWriter;

		/** 文件名 */
		private final String name;

		/** 最后一次写入的值 */
		private int theLatestNum;

		/** 优先级因子,值越大优先级越低 */
		private int priorityFactor = 0;

		public MockOutput(int capacity, String name, String path)
				throws IOException {
			this.stringBuilder = new StringBuilder(capacity);
			this.name = name;
			this.printWriter = new PrintWriter(new FileWriter(path + name
					+ ".txt"));
		}

		public int getTheLatestNum() {
			return theLatestNum;
		}

		public void setTheLatestNum(int theLatestNum) {
			this.theLatestNum = theLatestNum;
		}

		public int getPriorityFactor() {
			return priorityFactor;
		}

		/**
		 * 递增优先级因子的值,降低写入优先级
		 */
		private void reducePriority() {
			priorityFactor++;
		}

		/**
		 * 写入操作
		 * 
		 * @param i
		 * @return
		 */
		public MockOutput append(int i) {
			stringBuilder.append(i);
			printWriter.print(i);
			reducePriority();
			return this;
		}

		/**
		 * 关闭文本输出
		 */
		public void close() {
			printWriter.close();
		}

		public String toString() {
			return name + ": " + stringBuilder.toString();
		}
	}

	/**
	 * 用于平衡写入优先级的比较器
	 * 
	 * @author: yanxuxin
	 * @date: 2010-2-24
	 */
	private class PriorityComparator implements Comparator<MockOutput> {

		public int compare(MockOutput o1, MockOutput o2) {
			int pf1 = o1.getPriorityFactor();
			int pf2 = o2.getPriorityFactor();
			return pf1 - pf2;
		}

	}

}